﻿using System.Collections.Generic;
using AutoMapper;
using Lucid.ESA.ClientDataSystem.Application.Contracts;
using Lucid.ESA.ClientDataSystem.Application.DataTransferObjects;
using Lucid.ESA.ClientDataSystem.Application.Requests;
using Lucid.ESA.ClientDataSystem.Application.Responses;
using Lucid.ESA.ClientDataSystem.Data.Common;
using Lucid.ESA.ClientDataSystem.Domain.Common;
using Lucid.ESA.ClientDataSystem.Domain.Entities;
using Lucid.ESA.ClientDataSystem.Domain.ParameterClasses;
using Lucid.ESA.ClientDataSystem.Domain.RepositoryContracts;
using StructureMap;

namespace Lucid.ESA.ClientDataSystem.Application.Implementations
{
    public class ContactService : IContactService
    {
        public Response GetContactById(long id)
        {
            //Load required repositories.
            IContactRepository contactRepository = ObjectFactory.GetInstance<IContactRepository>();

            //Get the entity.
            Contact contact = contactRepository.GetById(id);

            //Map to DTO.
            Mapper.Reset();
            Mapper.CreateMap<Contact, ContactDto>();
            Mapper.CreateMap<Client, ClientDto>()
                .ForMember(prop => prop.Sites, opt => opt.Ignore())
                .ForMember(prop => prop.Contacts, opt => opt.Ignore());
            ContactDto contactDto = Mapper.Map<Contact, ContactDto>(contact);

            //Return DTO in a response object.
            return new GetResponse<ContactDto>(contactDto);
        }

        public Response GetAllContacts()
        {
            //Load required repositories.
            IContactRepository contactRepository = ObjectFactory.GetInstance<IContactRepository>();

            //Get the entity.
            List<Contact> contacts = contactRepository.GetAll();

            //Map to DTO.
            Mapper.Reset();
            Mapper.CreateMap<Contact, ContactDto>();
            Mapper.CreateMap<Client, ClientDto>()
                .ForMember(prop => prop.Sites, opt => opt.Ignore())
                .ForMember(prop => prop.Contacts, opt => opt.Ignore());
            List<ContactDto> contactDtos = Mapper.Map<List<Contact>, List<ContactDto>>(contacts);

            //Return DTO in a response object.
            return new GetResponse<List<ContactDto>>(contactDtos);
        }

        public Response CreateContact(CreateContactRequest request)
        {
            //Load required repositories.
            IClientRepository clientRepository = ObjectFactory.GetInstance<IClientRepository>();
            IContactRepository contactRepository = ObjectFactory.GetInstance<IContactRepository>();

            //Create the parameter object from the request object, mapping any primitive types.
            Mapper.Reset();
            Mapper.CreateMap<CreateContactRequest, CreateContactParameters>()
                .ForMember(prop => prop.Client, opt => opt.Ignore());
            CreateContactParameters parameters = Mapper.Map<CreateContactRequest, CreateContactParameters>(request);

            //Set the parameters with any required entities. Entities are 'Loaded', not 'Got' so only a proxy is returned with the Id. 
            //This way, it does not need to hit the database.
            parameters.Client = clientRepository.LoadById(request.ClientId);

            //Create the entity.
            Contact contact = Contact.Create(parameters);

            //Validate the entity, and return ValidationErrorResponse if fails.
            ValidationMessageCollection messages = contact.Validate();
            if (messages.Count > 0) return new ValidationErrorResponse(messages);

            //Save the entity.
            using (ITransactionProvider transactionProvider = ObjectFactory.GetInstance<ITransactionProvider>())
            {
                transactionProvider.BeginTransaction();
                contactRepository.Save(contact);
                transactionProvider.CommitTransaction();
            }

            //Return a successful response (will only get here if no exception is received).
            return new CreateResponse(contact.Id.Value);
        }

        public Response EditContact(EditContactRequest request)
        {
            //Load required repositories.
            IContactRepository contactRepository = ObjectFactory.GetInstance<IContactRepository>();

            //Create the parameter object from the request object, mapping any primitive types.
            Mapper.Reset();
            Mapper.CreateMap<EditContactRequest, EditContactParameters>();
            EditContactParameters parameters = Mapper.Map<EditContactRequest, EditContactParameters>(request);

            //Get the entity.
            Contact contact = contactRepository.GetById(request.ContactId);

            //Edit the entity.
            contact.Edit(parameters);

            //Validate the entity, and return ValidationErrorResponse if fails.
            ValidationMessageCollection messages = contact.Validate();
            if (messages.Count > 0) return new ValidationErrorResponse(messages);

            //Save the entity.
            using (ITransactionProvider transactionProvider = ObjectFactory.GetInstance<ITransactionProvider>())
            {
                transactionProvider.BeginTransaction();
                contactRepository.SaveOrUpdate(contact);
                transactionProvider.CommitTransaction();
            }

            //Return a successful response (will only get here if no exception is received).
            return new Response(ResponseStatus.Success);
        }
    }
}
